#light

open System
open System.Collections.Generic
open System.Runtime.InteropServices
open MPI
open Dwarf.Managed

[<DllImport(@"CliTools.dll")>]
extern void start()

[<DllImport(@"CliTools.dll")>]
extern double finish()

[<DllImport(@"CliTools.dll")>]
extern void usage()

open MapReduceFSharpMethod

let initParallelization(mainContent : string, threadCount : int) : string[] =
    let mutable mpiBounds = Array.zeroCreate<string> threadCount
    let mutable contentSize = (int) mainContent.Length
    let interval = (int) 1 + contentSize / threadCount
    let mutable leftBoard = (int) 0
    let mutable part = (int) 0
    while part < (threadCount - 1) do
        let mutable certainInterval = (int)interval
        while not (mainContent.[leftBoard + certainInterval] = ' ') do
            certainInterval <- certainInterval + 1
        certainInterval <- certainInterval + 1
        mpiBounds.[part] <- mainContent.Substring(leftBoard, certainInterval)
        part <- part + 1
        leftBoard <- leftBoard + certainInterval
    mpiBounds.[part] <- mainContent.Substring(leftBoard)
    mpiBounds

let sumThreadResult(dictionaryFirst : IDictionary<string, int>) (dictionarySecond : IDictionary<string, int>) : IDictionary<string, int> =
    let mutable stringTotal = dictionaryFirst
    for pair in dictionarySecond do
        let mutable value = pair.Value
        let key = pair.Key
        if stringTotal.ContainsKey(key) then
            value <- value + stringTotal.[key]
        stringTotal.Remove(key)
        stringTotal.Add(key, value)
    stringTotal

let solve(content : string, root : int) : SortedDictionary<String, int> =
    let contentDictionary = content |> map |> reduce
    let op = new ReductionOperation<IDictionary<string, int>>(sumThreadResult)
    MPI.Intracommunicator.world.Reduce<IDictionary<string, int>>(contentDictionary, op, root)    
    new SortedDictionary<string, int>(contentDictionary, StringComparer.Ordinal)

let main() =
    let args = System.Environment.GetCommandLineArgs()
    let mutable mpiBounds = null : string[]
    let mutable dwarfConfigurator = null

    let mpienv = new MPI.Environment(ref args)
    let rank = MPI.Communicator.world.Rank
    let root = 0 : int

    start()

    if rank = root then
        dwarfConfigurator <- new Configurator(args)
        dwarfConfigurator.WriteSettings()
        let mainContent = dwarfConfigurator.GetData();
        mpiBounds <- initParallelization(mainContent, MPI.Communicator.world.Size)

    MPI.Intracommunicator.world.Broadcast<string>(&mpiBounds, root)
    let stringTotal = solve(mpiBounds.[rank], root)

    let time = finish()

    if rank = root then
        dwarfConfigurator.Close(time, stringTotal)
    mpienv.Dispose()

main()